package gateway.filter;

import gateway.utils.JwtUtil;
import gateway.config.IgnoreWhiteProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Map;
import org.springframework.http.server.reactive.ServerHttpRequest;
import java.util.Arrays;
import java.util.List;

@Component
public class AuthFilter implements GlobalFilter, Ordered {
    
    private static final Logger log = LoggerFactory.getLogger(AuthFilter.class);
    
    // 后台路径前缀列表
    private static final List<String> ADMIN_PATH_PREFIXES = Arrays.asList(
        "/jiang",       // 主后台路径
        "/order/jiang", // 订单后台路径
        "/goods/jiang", // 商品后台路径
        "/shop/jiang",  // 商铺后台路径
        "/auth/jiang",   // 认证后台路径
        "/files/jiang",   // 文件后台路径
        "/comment/jiang",  // 评论后台路径
        "/cart/jiang/",  // 购物车后台路径
        "/chat/admin",
        "/chat/close",
        "/chat/getUserChatHistory"
    );
    
    @Autowired
    private IgnoreWhiteProperties ignoreWhiteProperties;
    
    @Autowired
    private JwtUtil jwtUtil;
    
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String path = exchange.getRequest().getURI().getPath();
        String requestId = java.util.UUID.randomUUID().toString().substring(0, 8);
        log.info("[{}] 开始验证请求路径: {}", requestId, path);
        
        // 如果是后台路径，由AdminFilter处理，此处直接放行
        if (isAdminPath(path)) {
            log.info("[{}] 路径 {} 是后台路径，由AdminFilter处理，直接放行", requestId, path);
            return chain.filter(exchange);
        }
        
        // 如果是公开接口，直接放行
        if (isPublicEndpoint(path)) {
            log.info("[{}] 路径 {} 是公开接口，直接放行", requestId, path);
            return chain.filter(exchange);
        }

        // 获取token
        String authHeader = null;
        String accessToken = null;

        String tokenFromQuery = exchange.getRequest().getQueryParams().getFirst("token");
        if (tokenFromQuery != null && !tokenFromQuery.isEmpty()) {
            accessToken = tokenFromQuery;
            log.info("[{}] 从查询参数获取到token: {}", requestId, maskToken(accessToken));
        } else {
            // 原有的从头部获取token的逻辑
            authHeader = exchange.getRequest().getHeaders().getFirst("Authorization");
            if (authHeader != null && authHeader.startsWith("Bearer ")) {
                accessToken = authHeader.substring(7);
                log.info("[{}] 从Authorization头部获取到token: {}", requestId, maskToken(accessToken));
            } else {
                // 尝试从token头部获取
                accessToken = exchange.getRequest().getHeaders().getFirst("token");
                if (accessToken != null) {
                    log.info("[{}] 从token头部获取到token: {}", requestId, maskToken(accessToken));
                }
            }
        }
        
        // 如果没有token，返回401
        if (accessToken == null || accessToken.isEmpty()) {
            log.warn("[{}] 请求路径 {} 缺少token，Authorization: {}, token: {}", 
                    requestId, path, authHeader, exchange.getRequest().getHeaders().getFirst("token"));
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        
        // 验证token
        try {
            // 1. 验证token格式和有效性
            log.info("[{}] 开始验证token格式", requestId);
            if (!jwtUtil.validateToken(accessToken)) {
                log.warn("[{}] token格式验证失败", requestId);
                exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                return exchange.getResponse().setComplete();
            }
            log.info("[{}] token格式验证通过", requestId);
            
            // 2. 检查token是否在黑名单中
            String blacklistKey = "token:blacklist";
            Boolean isBlacklisted = stringRedisTemplate.opsForSet().isMember(blacklistKey, accessToken);
            if (Boolean.TRUE.equals(isBlacklisted)) {
                log.warn("[{}] token在黑名单中", requestId);
                exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                return exchange.getResponse().setComplete();
            }
            log.info("[{}] token不在黑名单中", requestId);
            
            // 3. 从token中获取用户信息并添加到请求头
            Map<String, Object> claims = jwtUtil.parseToken(accessToken);
            log.debug("[{}] 解析token获取到claims: {}", requestId, claims.keySet());
            
            // 安全获取claim值，避免空指针异常
            String userId = getClaimValueSafely(claims, "id");
            String nickName = getClaimValueSafely(claims, "nickName");
            
            log.debug("[{}] 从token中提取的用户信息 - userId: {}, nickName: {}", requestId, userId, nickName);
            
            // 构建新的请求，添加用户信息到请求头
            ServerHttpRequest mutatedRequest = exchange.getRequest().mutate()
                    .header("uid", userId)
                    .header("request-id", requestId)
                    .header("userType", "user")
                    .build();
            
            // 如果昵称不为空，添加到请求头
            if (nickName != null && !nickName.isEmpty()) {
                mutatedRequest = mutatedRequest.mutate()
                        .header("nickName", nickName)
                        .build();
            }
            log.info("[{}] token验证全部通过，继续处理请求", requestId);
            
            // 使用修改后的请求继续处理
            return chain.filter(exchange.mutate().request(mutatedRequest).build());
            
        } catch (Exception e) {
            log.error("[{}] token验证过程发生异常: {}", requestId, e.getMessage(), e);
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
    }
    
    /**
     * 判断是否是后台管理路径
     * @param path 请求路径
     * @return 是否是后台路径
     */
    private boolean isAdminPath(String path) {
        return ADMIN_PATH_PREFIXES.stream().anyMatch(path::startsWith);
    }
    
    /**
     * 安全获取claim值，避免空指针异常
     * @param claims JWT声明
     * @param key 键名
     * @return 值的字符串表示，如果不存在则返回空字符串
     */
    private String getClaimValueSafely(Map<String, Object> claims, String key) {
        Object value = claims.get(key);
        return value != null ? value.toString() : "";
    }
    
    /**
     * 掩码处理token，只显示前10个和后10个字符
     * @param token 原始token
     * @return 掩码处理后的token
     */
    private String maskToken(String token) {
        if (token == null || token.length() <= 20) {
            return "***masked***";
        }
        return token.substring(0, 10) + "..." + token.substring(token.length() - 10);
    }
    
    private boolean isPublicEndpoint(String path) {
        boolean isPublic = ignoreWhiteProperties.getWhites().stream()
                .anyMatch(path::startsWith);
        log.debug("检查路径 {} 是否为公开接口: {}", path, isPublic);
        return isPublic;
    }
    
    @Override
    public int getOrder() {
        return 0;
    }
} 